/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package info.threads;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;

/**
 *
 * @author Martin
 */
public class Billiard extends javax.swing.JFrame {
    Thread animation;
    boolean alive=true;
    int X,Y;
    double SinX=0.0, SinY=0.0;
    double SinOffX = 2*Math.PI / 180;
    double SinOffY = 5*Math.PI / 180;
    boolean running=false;
    private Image imBuffer;
    private Graphics grBuffer;
    private long sleepTime=40; 
    public int width;
    public int height;
    private final ArrayList<Ball> balls;
    public Billiard() {
        initComponents();
        width=pnAct.getWidth();
        height=pnAct.getHeight();
        imBuffer = createImage(width,height);
        grBuffer = imBuffer.getGraphics();
        animation=new Thread(new animationRunnable());
        balls=new ArrayList<Ball>();
        balls.add(new Ball(Color.blue,height/2,width/3,new Speed(10,2)));
        balls.add(new Ball(Color.yellow,100,200,new Speed(8,9)));
        balls.add(new Ball(Color.red,height/3,width/3,new Speed(2,8)));
        balls.add(new Ball(Color.CYAN,100,200,new Speed(2,6)));
        balls.add(new Ball(Color.GRAY,height/4,width/3,new Speed(8,2)));
        balls.add(new Ball(Color.WHITE,100,200,new Speed(3,5)));
        balls.add(new Ball(Color.PINK,height/2,width/3,new Speed(5,6)));
        balls.add(new Ball(Color.MAGENTA,120,30,new Speed(3,5)));
        balls.add(new Ball(Color.ORANGE,190,200,new Speed(3,5)));
        balls.add(new Ball(Color.BLUE,height/5,width/2,new Speed(2,11)));
        balls.add(new Ball(Color.GREEN,120,300,new Speed(5,5)));
        
    }
    
    class animationPanel extends JPanel {
        public void paint(Graphics g){
            if(running){
                grBuffer.setColor(this.getBackground());
                grBuffer.fillRect(0,0,pnAct.getWidth(),pnAct.getHeight());
                grBuffer.setColor(Color.green);
                grBuffer.fillRect(0+15, 0+15, width-30, height-30);
                
                Iterator i=balls.iterator();
                while(i.hasNext()){
                    Ball b=((Ball) i.next());
                    if (b.speed.getxDirection() == Speed.DIRECTION_RIGHT
				&& b.x + b.size / 2 >= width-30) {
			b.speed.toggleXDirection();
                    }
                    if (b.speed.getxDirection() == Speed.DIRECTION_LEFT && b.x - b.size / 2 <= 0+15) {
			b.speed.toggleXDirection();
                    }
                    if (b.speed.getyDirection() == Speed.DIRECTION_DOWN
                                    && b.y + b.size / 2 >= height-30) {
                            b.speed.toggleYDirection();
                    }
                    if (b.speed.getyDirection() == Speed.DIRECTION_UP
                                    && b.y - b.size / 2 <= 0+15) {
                            b.speed.toggleYDirection();
                    }
                    b.update();
                    b.draw(grBuffer);
                }
                g.drawImage(imBuffer, 0, 0, null);
            }
        }
    }

    private class Ball {
        private int x,y;
        private Color col;
        private int size=20;
        Speed speed=new Speed(2,2);
        
        public Ball(Color col, int x, int y,Speed speed){
            this.col=col;
            this.x=x;
            this.y=y;
            this.speed=speed;
        }
        
        public void draw(Graphics g){
            g.setColor(col);
            g.fillOval(x, y, size, size);
        }
        
        public void update(){
            x += (speed.getXv() * speed.getxDirection());
            y += (speed.getYv() * speed.getyDirection());
        }
        
    }
    
    public class Speed {

	public static final int DIRECTION_RIGHT	= 1;
	public static final int DIRECTION_LEFT	= -1;
	public static final int DIRECTION_UP	= -1;
	public static final int DIRECTION_DOWN	= 1;

	private float xv = 2;	// velocity value on the X axis
	private float yv = 20;	// velocity value on the Y axis

	private int xDirection = DIRECTION_RIGHT;
	private int yDirection = DIRECTION_DOWN;

	public Speed() {
		this.xv = 1;
		this.yv = 1;
	}

	public Speed(float xv, float yv) {
		this.xv = xv;
		this.yv = yv;
	}

	public float getXv() {
		return xv;
	}
	public void setXv(float xv) {
		this.xv = xv;
	}
	public float getYv() {
		return yv;
	}
	public void setYv(float yv) {
		this.yv = yv;
	}

	public int getxDirection() {
		return xDirection;
	}
	public void setxDirection(int xDirection) {
		this.xDirection = xDirection;
	}
	public int getyDirection() {
		return yDirection;
	}
	public void setyDirection(int yDirection) {
		this.yDirection = yDirection;
	}
	public void toggleXDirection() {
		xDirection = xDirection * -1;
	}
	public void toggleYDirection() {
		yDirection = yDirection * -1;
	}
}
    
    
    public class animationRunnable implements Runnable{
        @Override
        public void run(){
            while(alive){
                if(running){
                    pnAct.repaint();
                }
                try {
                    new Thread().sleep(sleepTime);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Billiard.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    public static double clamp(double val, double min, double max) {
        if(val<=min){
            return min;
        } else if(val>=max){
            return max;
        } else {
            return val;
        }
    }
    
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jSeparator1 = new javax.swing.JSeparator();
        jLabel1 = new javax.swing.JLabel();
        pnSett = new javax.swing.JPanel();
        slUpdateSpeed = new javax.swing.JSlider();
        bnStart = new javax.swing.JButton();
        bnPause = new javax.swing.JButton();
        pnAct = new animationPanel();
        jSeparator2 = new javax.swing.JSeparator();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);

        jLabel1.setFont(new java.awt.Font("Tahoma", 1, 18)); // NOI18N
        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel1.setText("Billiard");

        slUpdateSpeed.setMajorTickSpacing(10);
        slUpdateSpeed.setPaintLabels(true);
        slUpdateSpeed.setPaintTicks(true);
        slUpdateSpeed.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                slUpdateSpeedStateChanged(evt);
            }
        });

        bnStart.setText("Start");
        bnStart.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                bnStartActionPerformed(evt);
            }
        });

        bnPause.setText("Pause");
        bnPause.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                bnPauseActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout pnSettLayout = new javax.swing.GroupLayout(pnSett);
        pnSett.setLayout(pnSettLayout);
        pnSettLayout.setHorizontalGroup(
            pnSettLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnSettLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(pnSettLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(slUpdateSpeed, javax.swing.GroupLayout.DEFAULT_SIZE, 213, Short.MAX_VALUE)
                    .addGroup(pnSettLayout.createSequentialGroup()
                        .addGroup(pnSettLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(bnStart, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(bnPause, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE))
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        pnSettLayout.setVerticalGroup(
            pnSettLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnSettLayout.createSequentialGroup()
                .addGap(32, 32, 32)
                .addComponent(slUpdateSpeed, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(bnStart)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(bnPause)
                .addContainerGap(208, Short.MAX_VALUE))
        );

        pnAct.setBackground(new java.awt.Color(51, 51, 51));

        javax.swing.GroupLayout pnActLayout = new javax.swing.GroupLayout(pnAct);
        pnAct.setLayout(pnActLayout);
        pnActLayout.setHorizontalGroup(
            pnActLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 466, Short.MAX_VALUE)
        );
        pnActLayout.setVerticalGroup(
            pnActLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 0, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnAct, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(pnSett, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 713, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(14, 14, 14)
                        .addComponent(pnSett, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(0, 10, Short.MAX_VALUE))
                    .addComponent(jSeparator1)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(pnAct, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addContainerGap())))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void slUpdateSpeedStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_slUpdateSpeedStateChanged
        this.sleepTime=(long)slUpdateSpeed.getValue();
    }//GEN-LAST:event_slUpdateSpeedStateChanged

    private void bnStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnStartActionPerformed
        if(animation!=null&&!animation.isAlive()){
            SinX=0.0;
            SinY=0.0;
            alive=true;
            running=true;
            animation=new Thread(new animationRunnable());
            animation.start();
            bnStart.setText("Stop");
        } else {
            alive=false;
            grBuffer.setColor(this.getBackground());
            grBuffer.fillRect(0,0,pnAct.getWidth(),pnAct.getHeight());
            bnStart.setText("Start");
        }
    }//GEN-LAST:event_bnStartActionPerformed

    private void bnPauseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPauseActionPerformed
        if(running)
            running=false;
        else
            running=true;
    }//GEN-LAST:event_bnPauseActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(Billiard.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(Billiard.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(Billiard.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(Billiard.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Billiard().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton bnPause;
    private javax.swing.JButton bnStart;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JPanel pnAct;
    private javax.swing.JPanel pnSett;
    private javax.swing.JSlider slUpdateSpeed;
    // End of variables declaration//GEN-END:variables
}
